﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Sencha.Serialization.Contracts
{
	public sealed class TypeDescription : IEquatable<TypeDescription>
	{
		public readonly static Type[] Numbers = new Type[] { typeof(Byte), typeof(SByte), typeof(Decimal), typeof(Single), typeof(Double), typeof(Int16), typeof(Int32), typeof(Int64), typeof(UInt16), typeof(UInt32), typeof(UInt64), typeof(Byte?), typeof(SByte?), typeof(Decimal?), typeof(Single?), typeof(Double?), typeof(Int16?), typeof(Int32?), typeof(Int64?), typeof(UInt16?), typeof(UInt32?), typeof(UInt64?) };

		private static readonly IList<MemberDescription> EmptyMembers = new ReadOnlyCollection<MemberDescription>(new MemberDescription[0]);

		private readonly Type m_type;

		/// <summary>
		/// Get type's name for serialization. Usually it is type's name, but can be overriden by attributes
		/// </summary>
		public string Name { get; internal set; }
		/// <summary>
		/// Get type's full name. Taken from <see cref="Type.FullName"/>
		/// </summary>
		public string TypeFullName { get; internal set; }
		/// <summary>
		/// Get type's base type. Null for "System.Object", and "System.Object" for interfaces
		/// </summary>
		public TypeDescription BaseType { get; internal set; }
		/// <summary>
		/// Get underlying type for Nullable, Enumerable, Dictionary, Enum types
		/// </summary>
		public TypeDescription UnderlyingType { get; internal set; }
		/// <summary>
		/// Get type's members(fields and properties). Inherited is not included
		/// </summary>
		public IList<MemberDescription> Members { get; internal set; }
		/// <summary>
		/// Get type's custom attributes. Inherited is not included
		/// </summary>
		public IList<Attribute> Attributes { get; internal set; }

		/// <summary>
		/// Is it compiler-generated anonymous type
		/// </summary>
		public bool IsAnonymousType { get; internal set; }
		/// <summary>
		/// Is not-public type
		/// </summary>
		public bool IsPrivate { get; internal set; }
		/// <summary>
		/// Is it by-ref type or <seealso cref="Nullable"/> type
		/// </summary>
		public bool IsNullable { get; internal set; }
		/// <summary>
		/// Is it special "TypeSerializer" type <seealso cref="TypeSerializer<>"/>
		/// </summary>
		public bool IsTypeSerializer { get; internal set; }
		/// <summary>
		/// Is it build-in type
		/// </summary>
		public bool IsPrimitive { get; internal set; }
		/// <summary>
		/// Is it by-ref type
		/// </summary>
		public bool IsReferenceType { get; internal set; }
		/// <summary>
		/// Is it value type
		/// </summary>
		public bool IsValueType { get; internal set; }
		/// <summary>
		/// Is it array type
		/// </summary>
		public bool IsArray { get; internal set; }
		/// <summary>
		/// Is it build-in .NET type
		/// </summary>
		public bool IsBuiltIn { get; internal set; }
		/// <summary>
		/// Is it enumerable. Special case is dictionary and string which is false regardless interface
		/// </summary>
		public bool IsEnumerable { get; internal set; }
		/// <summary>
		/// Is it numeric type
		/// </summary>
		public bool IsNumeric { get; internal set; }
		/// <summary>
		/// Is it dictionary type. Tests for IDictionary interface and IDictionary`[Key,Value]
		/// </summary>
		public bool IsDictionary { get; internal set; }
		/// <summary>
		/// Is it interface
		/// </summary>
		public bool IsInterface { get; internal set; }
		/// <summary>
		/// Is it <see cref="System.Object"/> type
		/// </summary>
		public bool IsObjectType { get; internal set; }
		/// <summary>
		/// Is it serializable type. Tests for <see cref="SerializableAttribute"/>
		/// </summary>
		public bool IsSerializable { get; internal set; }
		/// <summary>
		/// Is it <see cref="IJsonSerializable"/> type. Tests for interface implementation
		/// </summary>
		public bool IsJsonSerializable { get; internal set; }
		/// <summary>
		/// Is it convertible type. Test for <see cref="System.IConvertible"/> interface and <see cref="System.TypeConverterAttribute"/> on type
		/// </summary>
		public bool IsConvertible { get; internal set; }
		/// <summary>
		/// Get options of type's surrogate
		/// </summary>
		public TypeSerializerOptions SerializerOptions { get; internal set; }

		internal TypeDescription(Type contractedType)
		{
			if (contractedType == null)
				throw new ArgumentNullException("contractedType");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			m_type = contractedType;
			Members = EmptyMembers;
		}

		public Type GetClrType()
		{
			return m_type;
		}

		public bool IsDescendantOf(TypeDescription typeInfo)
		{
			if (typeInfo == null)
				throw new ArgumentNullException("typeInfo");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var baseType = this;
			while (baseType != null)
			{
				if (baseType.Equals(typeInfo))
					return true;

				baseType = baseType.BaseType;
			}
			return false;
		}

		#region IEquatable<TypeInfo> Members
		public override int GetHashCode()
		{
			return m_type.GetHashCode();
		}
		public override bool Equals(object obj)
		{
			if (Object.ReferenceEquals(this, obj))
				return true;
			if (obj == null)
				return false;

			return this.Equals(obj as TypeDescription);
		}

		public bool Equals(TypeDescription other)
		{
			if (other == null)
				return false;
			if (ReferenceEquals(this, other))
				return true;

			return Object.Equals(this.m_type, other.m_type);
		}

		#endregion

		public override string ToString()
		{
			return string.Format("{0}", Name);
		}


	}
}
